﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Reflection;
using OntologyUtility.Xml;
using System.Xml;
using System.Collections;

namespace OntologyFormDesigner
{
    public class Class1
    {
        static List<Type> GetInterfaceByType(Type type, Type nativeInterface)
        {
            List<Type> types = new List<Type>();
            foreach(Type tInterface in type.GetInterfaces()) 
            {
                if (tInterface.GetCustomAttributes(typeof(OntologyUtility.Xml.XmlNodeAttribute),true).Length != 0) //TODO: per ottimizzare devo inserire una decorazione a livello di classe
                    if (!tInterface.Equals(nativeInterface))
                        types.Add(tInterface);
            }
            return types;
        }
        static List<Type> GetObjectByInterface(Type _interface) 
        {
            List<Type> types = new List<Type>();
            foreach (Type _type in Assembly.GetExecutingAssembly().GetTypes())
            {
                try
                {
                    if (_interface.IsAssignableFrom(_type)) {
                        Object[] pInfo = _type.GetCustomAttributes(typeof(Annotation.WidgetAttribute), true);
                        if (pInfo.Length != 0)
                            types.Add(_type);
                    }
                }
                catch (Exception)
                { 
                    
                }
            }

            return types;
        }

        public void readXml(String xmlFile, ref Object form) {
            XDocument document = XDocument.Load(xmlFile);
            Object obj = new Widget.WidgetForm();
            funzione(document.Root.Element("WidgetForm"), typeof(OntologyUtility.Interfaces.IWidgetContainer), ref form);
        }


        private static void funzione(XElement element, Type type, ref Object obj)
        {
            
            PropertyInfo[] propertyInfo = type.GetProperties();
            for (int i = 0; i < propertyInfo.Length; i++)
            {
                object[] attributes = propertyInfo[i].GetCustomAttributes(true);

                for (int j = 0; j < attributes.Length; j++)
                {
                    if (attributes[j] is OntologyUtility.Xml.XmlNodeTypeAttribute)
                    {
                        if (((XmlNodeTypeAttribute)attributes[j]).Type.Equals(XmlNodeType.Element))
                            {
                                try
                                {
                                    if (typeof(IList).IsAssignableFrom(propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType))
                                    {   
                                        String ele = propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).Name;
                                        Type _type = propertyInfo[i].ReflectedType.GetProperty(ele).PropertyType.GetGenericArguments()[0];

                                        Console.WriteLine("TYPE: {0}, ELE: {1}", _type, ele );
                                        if (_type.IsInterface)
                                        {
                                            foreach (Type t in GetObjectByInterface(_type))
                                            {
                                                try
                                                {
                                                    Object[] pInfo = t.GetCustomAttributes(typeof(Annotation.WidgetAttribute), true);
                                                    if (pInfo.Length != 0)
                                                    {
                                                        foreach (XElement _element in element.Element(ele).Elements((pInfo[0] as Annotation.WidgetAttribute).WidgetName))  //ok ma se l'oggetto implenta altre interfaccie
                                                        {
                                                            Object newObject = t.GetConstructor(new Type[] { }).Invoke(new object[] { });
                                                            funzione(_element, _type, ref newObject); //fill newObject
                                                            foreach (Type tInterface in GetInterfaceByType(t, _type))
                                                                funzione(_element, tInterface, ref newObject);
                                                            if (typeof(IList).IsAssignableFrom(obj.GetType().GetProperty(propertyInfo[i].Name).GetValue(obj, new Object[0]).GetType()))
                                                            {
                                                                ((IList)obj.GetType().GetProperty(propertyInfo[i].Name).GetValue(obj, new Object[0])).Add(newObject);
                                                            }
                                                            //else TODO:
                                                        }
                                                    }
                                                }
                                                catch (Exception ex)
                                                {
                                                    Console.WriteLine("Ex: " + ex.ToString());
                                                }
                                            }
                                        }
                                        else // è un oggetto nn una interfaccia quindi lo posso instanziare direttamente -- ma è sempre una lista!!!
                                            //quindi devo passare il metodo giusto perche devo ricerare l'elemento giusto!!! l'elemento che devo cercare è il tipo della lista
                                        {
                                            

                                            foreach (XElement _element in element.Element(ele).Elements(_type.Name))
                                            {
                                                Object _obj = _type.GetConstructor(new Type[] { }).Invoke(new object[] { });

                                                funzione( //fill _obj
                                                    _element,
                                                    _type,
                                                    ref _obj);

                                                if (typeof(IList).IsAssignableFrom(obj.GetType().GetProperty(propertyInfo[i].Name).GetValue(obj, new Object[0]).GetType()))
                                                {
                                                    ((IList)obj.GetType().GetProperty(propertyInfo[i].Name).GetValue(obj, new Object[0])).Add(_obj);
                                                }
                                                else
                                                {
                                                    //set _obj to Obj!!! //se arrivo qua devo inserire l'elemento in una lista...
                                                    obj.GetType().GetProperty(propertyInfo[i].Name).SetValue(
                                                        obj,
                                                        _obj,
                                                        new Object[0]
                                                    );
                                                }
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType.IsInterface)
                                        {
                                        }
                                        else
                                        {
                                            Type _type = propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType;
                                            Object _obj = _type.GetConstructor(new Type[] { }).Invoke(new object[] { });

                                            funzione( //fill _obj
                                                element.Element(propertyInfo[i].Name),
                                                propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType,
                                                ref _obj);

                                            //set _obj to Obj!!!
                                            obj.GetType().GetProperty(propertyInfo[i].Name).SetValue(
                                                obj,
                                                _obj,
                                                new Object[0]
                                            );
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine("Element Exception: {0}", e.ToString());
                                }
                            }
                            else if (((XmlNodeTypeAttribute)attributes[j]).Type.Equals(XmlNodeType.Attribute))
                            {
                                try
                                {
                                    Console.WriteLine("Attribute {0}:", propertyInfo[i].Name);
                                    Console.WriteLine("Attribute Value {0}:", element.Attribute(propertyInfo[i].Name).Value);
                                    //obj.GetType().InvokeMember(propertyInfo[i].Name, BindingFlags.GetProperty, null, obj, new Object[] { element.Attribute(propertyInfo[i].Name).Value});
                                    if (typeof(System.String).IsAssignableFrom(propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType))
                                    {

                                        obj.GetType().GetProperty(propertyInfo[i].Name).SetValue(
                                            obj,
                                            element.Attribute(propertyInfo[i].Name).Value,
                                            new Object[0]
                                        );
                                    }
                                    else if (typeof(System.Int32).IsAssignableFrom(propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType))
                                    {
                                        Console.WriteLine("");
                                        obj.GetType().GetProperty(propertyInfo[i].Name).SetValue(
                                            obj,
                                            Int32.Parse(element.Attribute(propertyInfo[i].Name).Value),
                                            new Object[0]
                                        );
                                    }
                                    else
                                        Console.WriteLine("Sono sono ne stringa ne int************************* {0} ", propertyInfo[i].ReflectedType.GetProperty(propertyInfo[i].Name).PropertyType);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine("Attribute exception: {0}", e.ToString());
                                }
                            }
                       
                    }
                }

            }
        }
    }
}
